using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace MoEngine
{
    public class SceneNode
    {
        protected Scene m_Scene;
        public Scene Scene
        {
            get
            {
                return m_Scene;
            }

            set
            {
                if (m_Scene != value)
                {
                    m_Scene = value;

                    foreach (SceneNode child in m_ChildNodes)
                    {
                        child.Scene = value;
                    }
                }
            }
        }   
     
        protected SceneNode m_Parent;
        public SceneNode Parent
        {
            get
            {
                return m_Parent;
            }
            set
            {
                if (m_Parent != value)
                {
                    m_Parent = value;
                    Scene = ((value != null) ? value.Scene : null);

                    IsTransformDirty = true;
                }    
            }
        }

        protected SceneNodeCollection m_ChildNodes = new SceneNodeCollection();

        public void Attach(SceneNode sceneNode)
        {
            // TODO: prevent circular attachment

            m_ChildNodes.Add(sceneNode);
            sceneNode.Parent = this;
        }

        public bool Detach(SceneNode sceneNode)
        {
            if (m_ChildNodes.Remove(sceneNode))
            {
                sceneNode.Parent = null;
                return true;
            }
            else
            {
                return false;
            }
        }
       
        #region Transformation

        protected Vector3 m_Translation = Vector3.Zero;

        public Vector3 Translation
        {
            get
            {
                return m_Translation;
            }
            set
            {
                if (m_Translation != value)
                {
                    m_Translation = value;
                    m_IsTransformDirty = true;
                }
            }
        }

        protected Matrix m_Rotation = Matrix.Identity;

        public Matrix Rotation
        {
            get
            {
                return m_Rotation;
            }
            set
            {
                if (m_Rotation != value)
                {
                    m_Rotation = value;
                    m_IsTransformDirty = true;
                }
            }
        }

        public Vector3 m_Scale = Vector3.One;

        public Vector3 Scale
        {
            get
            {
                return m_Scale;
            }
            set
            {
                if (m_Scale != value)
                {
                    m_Scale = value;
                    m_IsTransformDirty = true;
                }
            }
        }

        public Matrix m_Transform = Matrix.Identity;

        public Matrix Transform
        {
            get { return m_Transform; }
        }

        public void UpdateTransform()
        {
            m_Transform =
                Matrix.CreateScale(Scale) * Rotation * 
                Matrix.CreateTranslation(Translation) *
                ((m_Parent == null) ? Matrix.Identity : m_Parent.Transform);
            m_IsTransformDirty = false;
        }

        bool m_IsTransformDirty = true;

        public bool IsTransformDirty
        {
            get { return m_IsTransformDirty; }
            set { m_IsTransformDirty = value; }
        }

        #endregion

        public virtual void Update()
        {
            if (m_IsTransformDirty)
            {
                UpdateTransform();

                foreach (SceneNode child in m_ChildNodes)
                {
                    child.IsTransformDirty = true;
                }
            }

            foreach (SceneNode child in m_ChildNodes)
            {
                child.Update();
            }
        }

        public virtual void Render() 
        {
            foreach (SceneNode child in m_ChildNodes)
            {
                child.Render();
            } 
        }
    }
}
